package com.yupi.springbootinit.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.constant.AttachmentConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.exception.ThrowUtils;
import com.yupi.springbootinit.mapper.AttachmentMapper;
import com.yupi.springbootinit.mapper.SalesOrderMapper;
import com.yupi.springbootinit.model.dto.attachment.AttachmentQueryRequest;
import com.yupi.springbootinit.model.dto.attachment.AttachmentUpdateRequest;
import com.yupi.springbootinit.model.entity.Attachment;
import com.yupi.springbootinit.model.entity.SalesOrder;
import com.yupi.springbootinit.model.vo.AttachmentVO;
import com.yupi.springbootinit.service.AttachmentService;
import com.yupi.springbootinit.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import jakarta.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 附件服务实现
 */
@Service
@Slf4j
public class AttachmentServiceImpl extends ServiceImpl<AttachmentMapper, Attachment>
        implements AttachmentService {

    @Resource
    private AttachmentMapper attachmentMapper;

    @Resource
    private SalesOrderMapper salesOrderMapper;

    @Value("${file.upload.path:D:/MES_FILES}")
    private String uploadRootPath;

    @Override
    public Attachment uploadFile(MultipartFile file, String bizType, Long bizId,
                                 String fileCategory, String node, String remark,
                                 Long uploaderId, String uploaderName) {
        // 校验文件
        validateFile(file);

        // 校验参数
        ThrowUtils.throwIf(StringUtils.isAnyBlank(bizType, fileCategory),
                ErrorCode.PARAMS_ERROR, "业务类型和文件分类不能为空");
        ThrowUtils.throwIf(ObjectUtils.anyNull(bizId, uploaderId),
                ErrorCode.PARAMS_ERROR, "业务ID和上传人ID不能为空");
        ThrowUtils.throwIf(StringUtils.isBlank(uploaderName),
                ErrorCode.PARAMS_ERROR, "上传人姓名不能为空");
        
        // 校验 bizType 和 fileCategory 不包含逗号（防止重复值拼接）
        if (bizType.contains(",")) {
            log.error("bizType 包含非法字符逗号: {}", bizType);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "业务类型格式错误，不能包含逗号");
        }
        if (fileCategory.contains(",")) {
            log.error("fileCategory 包含非法字符逗号: {}", fileCategory);
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件分类格式错误，不能包含逗号");
        }

        try {
            // 获取文件信息
            String originalFilename = file.getOriginalFilename();
            String fileExtension = getFileExtension(originalFilename);
            String storageName = generateStorageName(originalFilename);

            // 创建上传目录
            String uploadPath = createUploadPath(bizType, bizId, fileCategory);
            File uploadDir = new File(uploadPath);
            if (!uploadDir.exists()) {
                uploadDir.mkdirs();
            }

            // 保存文件
            String filePath = uploadPath + File.separator + storageName;
            file.transferTo(new File(filePath));

            // 创建附件记录
            Attachment attachment = new Attachment();
            attachment.setBizType(bizType);
            attachment.setBizId(bizId);
            attachment.setFileType(fileExtension);
            attachment.setFileCategory(fileCategory);
            attachment.setFileName(originalFilename);
            attachment.setStorageName(storageName);
            attachment.setFilePath(filePath);
            attachment.setFileSize((int) file.getSize());
            attachment.setUploaderId(uploaderId);
            attachment.setUploaderName(uploaderName);
            attachment.setUploadTime(new Date());
            attachment.setNode(node);
            attachment.setRemark(remark);
            attachment.setIsDelete(0);
            attachment.setCreateTime(new Date());
            attachment.setUpdateTime(new Date());

            // 保存到数据库
            boolean result = this.save(attachment);
            ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "附件保存失败");

            return attachment;
        } catch (IOException e) {
            log.error("文件上传失败", e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件上传失败");
        }
    }

    @Override
    public List<Attachment> batchUploadFiles(List<MultipartFile> files, String bizType, Long bizId,
                                             String fileCategory, String node, String remark,
                                             Long uploaderId, String uploaderName) {
        if (files == null || files.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件列表不能为空");
        }

        List<Attachment> attachments = new ArrayList<>();
        for (MultipartFile file : files) {
            if (!file.isEmpty()) {
                Attachment attachment = uploadFile(file, bizType, bizId, fileCategory,
                        node, remark, uploaderId, uploaderName);
                attachments.add(attachment);
            }
        }

        return attachments;
    }

    @Override
    public List<Attachment> getAttachmentsByBiz(String bizType, Long bizId) {
        return attachmentMapper.selectByBizTypeAndBizId(bizType, bizId);
    }

    @Override
    public List<Attachment> getAttachmentsByBizAndCategory(String bizType, Long bizId, String fileCategory) {
        return attachmentMapper.selectByBizTypeAndBizIdAndFileCategory(bizType, bizId, fileCategory);
    }

    @Override
    public List<Attachment> getAttachmentsByBizAndCategoryAndNode(String bizType, Long bizId,
                                                                  String fileCategory, String node) {
        return attachmentMapper.selectByBizTypeAndBizIdAndFileCategoryAndNode(bizType, bizId, fileCategory, node);
    }

    @Override
    public boolean deleteAttachment(Long attachmentId, Long deletedBy) {
        if (attachmentId == null || deletedBy == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID和删除人ID不能为空");
        }

        Attachment attachment = new Attachment();
        attachment.setId(attachmentId);
        attachment.setIsDelete(1);
        attachment.setDeleteTime(new Date());
        attachment.setDeletedBy(deletedBy);
        attachment.setUpdateTime(new Date());

        return this.updateById(attachment);
    }

    @Override
    public boolean batchDeleteAttachments(List<Long> attachmentIds, Long deletedBy) {
        if (attachmentIds == null || attachmentIds.isEmpty() || deletedBy == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID列表和删除人ID不能为空");
        }

        return attachmentMapper.batchDelete(attachmentIds, deletedBy) > 0;
    }

    @Override
    public QueryWrapper<Attachment> getQueryWrapper(AttachmentQueryRequest attachmentQueryRequest) {
        QueryWrapper<Attachment> queryWrapper = new QueryWrapper<>();
        if (attachmentQueryRequest == null) {
            return queryWrapper;
        }

        String bizType = attachmentQueryRequest.getBizType();
        Long bizId = attachmentQueryRequest.getBizId();
        String fileType = attachmentQueryRequest.getFileType();
        String fileCategory = attachmentQueryRequest.getFileCategory();
        String fileName = attachmentQueryRequest.getFileName();
        Long uploaderId = attachmentQueryRequest.getUploaderId();
        String uploaderName = attachmentQueryRequest.getUploaderName();
        String node = attachmentQueryRequest.getNode();
        Date uploadTimeStart = attachmentQueryRequest.getUploadTimeStart();
        Date uploadTimeEnd = attachmentQueryRequest.getUploadTimeEnd();
        String sortField = attachmentQueryRequest.getSortField();
        String sortOrder = attachmentQueryRequest.getSortOrder();

        // 拼接查询条件
        queryWrapper.eq(StringUtils.isNotBlank(bizType), "bizType", bizType);
        queryWrapper.eq(ObjectUtils.isNotEmpty(bizId), "bizId", bizId);
        queryWrapper.eq(StringUtils.isNotBlank(fileType), "fileType", fileType);
        queryWrapper.eq(StringUtils.isNotBlank(fileCategory), "fileCategory", fileCategory);
        queryWrapper.like(StringUtils.isNotBlank(fileName), "fileName", fileName);
        queryWrapper.eq(ObjectUtils.isNotEmpty(uploaderId), "uploaderId", uploaderId);
        queryWrapper.like(StringUtils.isNotBlank(uploaderName), "uploaderName", uploaderName);
        queryWrapper.eq(StringUtils.isNotBlank(node), "node", node);
        queryWrapper.ge(ObjectUtils.isNotEmpty(uploadTimeStart), "uploadTime", uploadTimeStart);
        queryWrapper.le(ObjectUtils.isNotEmpty(uploadTimeEnd), "uploadTime", uploadTimeEnd);
        queryWrapper.eq("isDelete", 0);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), "ascend".equals(sortOrder), sortField);

        return queryWrapper;
    }

    @Override
    public AttachmentVO getAttachmentVO(Attachment attachment) {
        if (attachment == null) {
            return null;
        }

        AttachmentVO attachmentVO = new AttachmentVO();
        BeanUtils.copyProperties(attachment, attachmentVO);

        // 格式化文件大小
        attachmentVO.setFileSizeFormatted(formatFileSize(attachment.getFileSize()));

        // 生成下载URL（这里可以根据实际需求生成）
        attachmentVO.setDownloadUrl("/attachment/download/" + attachment.getId());

        return attachmentVO;
    }

    @Override
    public Page<AttachmentVO> getAttachmentVOPage(Page<Attachment> attachmentPage) {
        List<Attachment> attachmentList = attachmentPage.getRecords();
        Page<AttachmentVO> attachmentVOPage = new Page<>(attachmentPage.getCurrent(),
                attachmentPage.getSize(), attachmentPage.getTotal());

        if (attachmentList.isEmpty()) {
            return attachmentVOPage;
        }

        List<AttachmentVO> attachmentVOList = attachmentList.stream()
                .map(this::getAttachmentVO)
                .collect(Collectors.toList());

        attachmentVOPage.setRecords(attachmentVOList);
        return attachmentVOPage;
    }

    @Override
    public void validateFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件不能为空");
        }

        // 检查文件大小
        if (file.getSize() > AttachmentConstant.MAX_FILE_SIZE) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,
                    "文件大小不能超过" + formatFileSize((int) AttachmentConstant.MAX_FILE_SIZE));
        }

        // 检查文件类型
        String originalFilename = file.getOriginalFilename();
        if (StringUtils.isBlank(originalFilename)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件名不能为空");
        }

        String fileExtension = getFileExtension(originalFilename).toLowerCase();
        boolean isAllowedType = Arrays.asList(AttachmentConstant.ALLOWED_FILE_TYPES)
                .contains(fileExtension);

        if (!isAllowedType) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR,
                    "不支持的文件类型: " + fileExtension);
        }
    }

    @Override
    public String formatFileSize(Integer fileSize) {
        if (fileSize == null || fileSize <= 0) {
            return "0 B";
        }

        final String[] units = {"B", "KB", "MB", "GB", "TB"};
        int digitGroups = (int) (Math.log10(fileSize) / Math.log10(1024));

        DecimalFormat decimalFormat = new DecimalFormat("#,##0.#");
        return decimalFormat.format(fileSize / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (StringUtils.isBlank(filename)) {
            return "";
        }
        int lastDotIndex = filename.lastIndexOf('.');
        if (lastDotIndex == -1) {
            return "";
        }
        return filename.substring(lastDotIndex + 1);
    }

    /**
     * 生成存储文件名
     */
    private String generateStorageName(String originalFilename) {
        String fileExtension = getFileExtension(originalFilename);
        String uuid = UUID.randomUUID().toString().replace("-", "");
        return uuid + (StringUtils.isNotBlank(fileExtension) ? "." + fileExtension : "");
    }

    /**
     * 创建上传路径 - 按订单类型/订单号/文件分类分级存储
     * 路径格式：{root}/{订单类型}/{订单号}/{文件分类}/
     */
    private String createUploadPath(String bizType, Long bizId, String fileCategory) {
        String orderNumber = getOrderNumberByBizId(bizType, bizId);
        String orderTypeDir = AttachmentConstant.BizType.SALES_ORDER.equals(bizType)
                ? AttachmentConstant.BizType.SALES_ORDER
                : (AttachmentConstant.BizType.RND_ORDER.equals(bizType) ? AttachmentConstant.BizType.RND_ORDER : bizType);
        String root = StringUtils.isBlank(uploadRootPath) ? ("D:" + File.separator + "MES_FILES") : uploadRootPath;

        // 添加文件分类作为子目录，便于文件管理
        String categoryDir = StringUtils.isNotBlank(fileCategory) ? fileCategory : "GENERAL";

        return root + File.separator + orderTypeDir + File.separator + orderNumber + File.separator + categoryDir;
    }

    /**
     * 根据业务ID获取订单号（支持销售订单与研发订单）
     */
    private String getOrderNumberByBizId(String bizType, Long bizId) {
        if (AttachmentConstant.BizType.SALES_ORDER.equals(bizType)) {
            SalesOrder salesOrder = salesOrderMapper.selectById(bizId);
            if (salesOrder != null && StringUtils.isNotBlank(salesOrder.getOrderNumber())) {
                return salesOrder.getOrderNumber();
            }
            return "SO_DEFAULT_" + bizId;
        }
        if (AttachmentConstant.BizType.RND_ORDER.equals(bizType)) {
            // TODO: 接入研发订单表与Mapper后，按ID查询研发订单号
            return "RD_DEFAULT_" + bizId;
        }
        // 其他业务按类型前缀占位，后续接入具体表
        return (StringUtils.isNotBlank(bizType) ? bizType : "BIZ") + "_DEFAULT_" + bizId;
    }

    @Override
    public boolean physicalDeleteAttachment(Long attachmentId, Long deletedBy) {
        if (attachmentId == null || deletedBy == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID和删除人ID不能为空");
        }

        // 获取附件信息
        Attachment attachment = this.getById(attachmentId);
        if (attachment == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "附件不存在");
        }

        try {
            // 删除本地文件
            if (StringUtils.isNotBlank(attachment.getFilePath())) {
                File file = new File(attachment.getFilePath());
                if (file.exists()) {
                    boolean fileDeleted = file.delete();
                    if (!fileDeleted) {
                        log.warn("本地文件删除失败: {}", attachment.getFilePath());
                    } else {
                        log.info("本地文件删除成功: {}", attachment.getFilePath());
                    }
                }
            }

            // 从数据库中物理删除
            boolean result = this.removeById(attachmentId);
            if (result) {
                log.info("附件物理删除成功: attachmentId={}, deletedBy={}", attachmentId, deletedBy);
            }
            return result;
        } catch (Exception e) {
            log.error("附件物理删除失败: attachmentId={}", attachmentId, e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "附件删除失败");
        }
    }

    @Override
    public boolean batchPhysicalDeleteAttachments(List<Long> attachmentIds, Long deletedBy) {
        if (attachmentIds == null || attachmentIds.isEmpty() || deletedBy == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID列表和删除人ID不能为空");
        }

        List<Attachment> attachments = this.listByIds(attachmentIds);
        if (attachments.isEmpty()) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "没有找到要删除的附件");
        }

        int successCount = 0;
        for (Attachment attachment : attachments) {
            try {
                if (physicalDeleteAttachment(attachment.getId(), deletedBy)) {
                    successCount++;
                }
            } catch (Exception e) {
                log.error("批量删除附件失败: attachmentId={}", attachment.getId(), e);
            }
        }

        log.info("批量物理删除附件完成: 总数={}, 成功={}", attachments.size(), successCount);
        return successCount > 0;
    }

    @Override
    public boolean updateAttachmentInfo(Long attachmentId, String fileCategory, String node, String remark, Long updatedBy) {
        if (attachmentId == null || updatedBy == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID和更新人ID不能为空");
        }

        // 获取原附件信息
        Attachment oldAttachment = this.getById(attachmentId);
        if (oldAttachment == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "附件不存在");
        }

        try {
            // 检查是否需要移动文件（文件分类发生变化）
            boolean needMoveFile = StringUtils.isNotBlank(fileCategory)
                    && !fileCategory.equals(oldAttachment.getFileCategory());

            String newFilePath = oldAttachment.getFilePath();
            if (needMoveFile) {
                // 创建新的存储路径
                String newUploadPath = createUploadPath(oldAttachment.getBizType(),
                        oldAttachment.getBizId(), fileCategory);
                File newUploadDir = new File(newUploadPath);
                if (!newUploadDir.exists()) {
                    newUploadDir.mkdirs();
                }

                // 移动文件
                File oldFile = new File(oldAttachment.getFilePath());
                if (oldFile.exists()) {
                    newFilePath = newUploadPath + File.separator + oldAttachment.getStorageName();
                    File newFile = new File(newFilePath);
                    boolean moved = oldFile.renameTo(newFile);
                    if (!moved) {
                        log.error("文件移动失败: {} -> {}", oldAttachment.getFilePath(), newFilePath);
                        throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件移动失败");
                    }
                    log.info("文件移动成功: {} -> {}", oldAttachment.getFilePath(), newFilePath);
                }
            }

            // 更新数据库记录
            Attachment updateAttachment = new Attachment();
            updateAttachment.setId(attachmentId);
            if (StringUtils.isNotBlank(fileCategory)) {
                updateAttachment.setFileCategory(fileCategory);
            }
            if (StringUtils.isNotBlank(node)) {
                updateAttachment.setNode(node);
            }
            if (StringUtils.isNotBlank(remark)) {
                updateAttachment.setRemark(remark);
            }
            if (needMoveFile) {
                updateAttachment.setFilePath(newFilePath);
            }
            updateAttachment.setUpdateTime(new Date());

            boolean result = this.updateById(updateAttachment);
            if (result) {
                log.info("附件信息更新成功: attachmentId={}, updatedBy={}", attachmentId, updatedBy);
            }
            return result;
        } catch (Exception e) {
            log.error("附件信息更新失败: attachmentId={}", attachmentId, e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "附件信息更新失败");
        }
    }

    @Override
    public boolean batchUpdateAttachments(List<AttachmentUpdateRequest> updateRequests, Long updatedBy) {
        if (updateRequests == null || updateRequests.isEmpty() || updatedBy == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "更新请求列表和更新人ID不能为空");
        }

        int successCount = 0;
        for (AttachmentUpdateRequest request : updateRequests) {
            try {
                if (updateAttachmentInfo(request.getId(), request.getFileCategory(),
                        request.getNode(), request.getRemark(), updatedBy)) {
                    successCount++;
                }
            } catch (Exception e) {
                log.error("批量更新附件失败: attachmentId={}", request.getId(), e);
            }
        }

        log.info("批量更新附件完成: 总数={}, 成功={}", updateRequests.size(), successCount);
        return successCount > 0;
    }

    @Override
    public Attachment replaceAttachmentFile(Long attachmentId, MultipartFile newFile, Long updatedBy) {
        if (attachmentId == null || newFile == null || newFile.isEmpty() || updatedBy == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "附件ID、新文件和更新人ID不能为空");
        }

        // 校验新文件
        validateFile(newFile);

        // 获取原附件信息
        Attachment oldAttachment = this.getById(attachmentId);
        if (oldAttachment == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "附件不存在");
        }

        try {
            // 备份原文件路径
            String oldFilePath = oldAttachment.getFilePath();

            // 获取新文件信息
            String originalFilename = newFile.getOriginalFilename();
            String fileExtension = getFileExtension(originalFilename);
            String storageName = generateStorageName(originalFilename);

            // 使用原有的存储路径
            String uploadPath = createUploadPath(oldAttachment.getBizType(),
                    oldAttachment.getBizId(), oldAttachment.getFileCategory());
            String newFilePath = uploadPath + File.separator + storageName;

            // 保存新文件
            newFile.transferTo(new File(newFilePath));

            // 删除旧文件
            if (StringUtils.isNotBlank(oldFilePath)) {
                File oldFile = new File(oldFilePath);
                if (oldFile.exists()) {
                    boolean deleted = oldFile.delete();
                    if (deleted) {
                        log.info("旧文件删除成功: {}", oldFilePath);
                    } else {
                        log.warn("旧文件删除失败: {}", oldFilePath);
                    }
                }
            }

            // 更新数据库记录
            Attachment updateAttachment = new Attachment();
            updateAttachment.setId(attachmentId);
            updateAttachment.setFileType(fileExtension);
            updateAttachment.setFileName(originalFilename);
            updateAttachment.setStorageName(storageName);
            updateAttachment.setFilePath(newFilePath);
            updateAttachment.setFileSize((int) newFile.getSize());
            updateAttachment.setUpdateTime(new Date());

            boolean result = this.updateById(updateAttachment);
            if (!result) {
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "附件信息更新失败");
            }

            log.info("附件文件替换成功: attachmentId={}, updatedBy={}", attachmentId, updatedBy);
            return this.getById(attachmentId);
        } catch (IOException e) {
            log.error("附件文件替换失败: attachmentId={}", attachmentId, e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件替换失败");
        } catch (Exception e) {
            log.error("附件文件替换失败: attachmentId={}", attachmentId, e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "附件替换失败");
        }
    }
}